#镜像使用规则：
#1. 每个镜像都有一个id，用于区分不同的镜像
#2. 每个镜像都有一个mirror_update_list，用于存储该镜像的所有mirror_update，每个mirror_update都有一个topic，用于订阅，一个update函数，用于更新镜像相应的数据。即一个镜像可以有多个mirror_update，每个mirror_update可以更新不同的数据。
#3.agent在主函数就已经创建，可以直接使用，不需要创建，在mission中创建mirror,并加入agent既可以使用。
from abc import ABC, abstractmethod
from sls_mirrors.sls_mqttClient import MQTTClient
import json

from typing import TypedDict

class MissionInfo(TypedDict):
    missionsType: str
    datasets: dict

#创建 MirrorUpdate的抽象类
class MirrorUpdate(ABC):
    """
    抽象类，用于更新镜像
    """
    def __init__(self,parent_mirror):
        self.parent_mirror = parent_mirror
        self.id =parent_mirror.id
        self.init_data()
        self.topic = self.get_sub_topic(self.id)
        self.notify_list = []

    def add_notify(self,func):
        self.notify_list.append(func)
    
    def remove_notify(self,func):
        #根据func删除notify_list中的元素
        self.notify_list.remove(func)
    
    def notify(self):
        # 通知所有订阅者,发送mirror和他的组件mirror_update
        for func in self.notify_list:
            func(self.parent_mirror,self)


    def update_with_notify(self,messageDict:dict):
        self.update(messageDict)
        self.notify()

    @abstractmethod
    def init_data(self):
        """
        初始化数据
        """
        pass
    @abstractmethod
    def get_sub_topic(self,id:str)-> str:
        """
        初始化主题
        """
        pass
    @abstractmethod
    def update(self,messageDict:dict):
        """
        更新镜像
        """
        pass

#作为 MIrror的组件之一，主要负责镜像与实体关于MissionSchudule的一些交互工作。
class ScheduleUpdate():
    """
        用于MissionSchedule的更新和交互，用户不应该修改该类的任何内容，只能使用。
    """
    def __init__(self,parent_mirror):
        self.parent_mirror = parent_mirror
        self.id =parent_mirror.id
        self.init_data()
        self.ms_topic_handle_list = []
        self.init_topics()
        self.publish_message = None
    
    def init_data(self):
        """
        初始化数据
        """
        self.runingTask = {
            "missionsType": None,
            "datasets": None
        }
        self.missionsIndex:int = None
        self.missionsList:list[MissionInfo] = []
        self.scheduleState = None

    def init_topics(self):
        """
        初始化主题
        """
        #创建正在执行任务的notify_list
        self.runingTask_notify_list = []
        self.add_topic_handle("/status/" + self.id + "/MS/runingTask",self.runingTask_handle,self.runingTask_notify_list)
        #创建任务列表信息
        self.missionsList_notify_list = []
        self.add_topic_handle("/status/" + self.id + "/MS/missionsList",self.missionsList_handle,self.missionsList_notify_list)
        #创建任务管理器状态信息
        self.scheduleState_notify_list = []
        self.add_topic_handle("/status/" + self.id + "/MS/scheduleState",self.scheduleState_handle,self.scheduleState_notify_list)

    
    def add_topic_handle(self,topic,handle,notify_list):
        """
        添加topic_handle
        """
        self.ms_topic_handle_list.append({"topic":topic,"handle":handle,"notify_list":notify_list})

    def add_notify(self,content:str,func):
        """
        添加notify,content为"runingTask","missionsList","scheduleState"中的一个，func为要添加的notify函数
        """
        topic = "/status/" + self.id + "/MS/" + content
        for item in self.ms_topic_handle_list:
            if item["topic"] == topic:
                item["notify_list"].append(func)
    
    def remove_notify(self,content:str,func):
        """
        删除notify,content为"runingTask","missionsList","scheduleState"中的一个，func为要删除的notify函数
        """
        topic = "/status/" + self.id + "/MS/" + content
        for item in self.ms_topic_handle_list:
            if item["topic"] == topic:
                item["notify_list"].remove(func)

    def notify(self,notify_list):
        """
        通知所有订阅者
        """
        for func in notify_list:
            func(self.parent_mirror,self)

    def set_pub_fuction(self,function):
        self.publish_message = function

    def ms_start(self):
        """
        启动MissionSchedule
        """
        commandDict = {"method": "start"}
        self.publish_message("/command/"+self.id+"/MS",commandDict)
    
    def ms_stop(self):
        """
        停止MissionSchedule
        """
        commandDict = {"method": "stop"}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_restart(self):
        """
        重启MissionSchedule
        """
        commandDict = {"method": "restart"}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_appendMission(self,missionDict:MissionInfo):
        """
        添加Mission 
        """
        commandDict = {"method": "append_mission","mission":missionDict}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_addMission(self,missionDict:MissionInfo,index:int):
        """
        添加Mission
        """ 
        commandDict = {"method": "add_mission","mission":missionDict,"index":index}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_deleteMission(self,index:int):
        """
        删除Mission
        """
        commandDict = {"method": "delete_mission","index":index}
        self.publish_message("/command/"+self.id+"/MS",commandDict)
    
    def ms_requestMissionsList(self):
        """
        请求MissionList
        """
        commandDict = {"method": "request_missions_list"}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_moveupMission(self,index):
        """
        上移Mission
        """
        commandDict = {"method": "moveup_mission","index":index}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def ms_movedownMission(self,index):
        """
        下移Mission
        """
        commandDict = {"method": "movedown_mission","index":index}
        self.publish_message("/command/"+self.id+"/MS",commandDict)

    def update(self,topic,messageDict:dict):
        """
        更新镜像
        """
        for item in self.ms_topic_handle_list:
            if item["topic"] == topic:
                item["handle"](messageDict)

    def runingTask_handle(self,messageDict:dict):
        """
        处理正在执行任务的信息
        """
        if "missionsType" in messageDict:
            self.runingTask["missionsType"] = messageDict["missionsType"]
        if "datasets" in messageDict:
            self.runingTask["datasets"] = messageDict["datasets"]
        if "missionsIndex" in messageDict:
            self.missionsIndex = messageDict["missionsIndex"]
        self.notify(self.runingTask_notify_list)

    def missionsList_handle(self,messageDict:dict):
        """
        处理任务列表信息
        """
        self.missionsList.clear()
        for missionDict in messageDict:
            self.missionsList.append(missionDict)
        self.notify(self.missionsList_notify_list)

    def scheduleState_handle(self,messageDict:dict):
        """
        处理任务管理器状态信息
        """
        self.missionsIndex = messageDict["index"]
        self.scheduleState = messageDict["state"]
        self.notify(self.scheduleState_notify_list)      



class Mirror():
    def __init__(self,id,*mirrorUpdateType):
        self.id = id
        self.mirror_update_list:list[MirrorUpdate] = []
        for mirror_update_type in mirrorUpdateType:
            #避免重复添加mirror_update
            if self.find_mirror_update_by_type(mirror_update_type) is not None:
                raise Exception("mirror_update_type already exists ,one mirror can only have one mirror_update_type of the same type")
            item = mirror_update_type(self)
            self.mirror_update_list.append(item)
            self.publish_fuction = None
        #创建ScheduleUpdate
        self.scheduleUpdate = ScheduleUpdate(self)

    def mirror_is_added_callback(self,publish_fuction):
        #给ScheduleUpdate设置publish_message函数
        self.scheduleUpdate.set_pub_fuction(publish_fuction)
        self.publish_fuction = publish_fuction

    #找到指定类型的mirror_update
    def find_mirror_update_by_type(self,type) -> MirrorUpdate:
        for item in self.mirror_update_list:
            if type == item.__class__:
                return item
        return None
    
    #添加mirror_update
    def add_mirror_updates(self,*mirror_update_types):
        for mirror_update_type in mirror_update_types:
            #避免重复添加mirror_update
            if self.find_mirror_update_by_type(mirror_update_type) is not None:
                raise Exception("mirror_update_type already exists ,one mirror can only have one mirror_update_type of the same type")            
            #创建实例
            item = mirror_update_type(self)
            self.mirror_update_list.append(item)

    #删除mirror_update
    def remove_mirror_updates(self,*mirror_update_types):
        for mirror_update_type in mirror_update_types:
            for item in self.mirror_update_list:
                if mirror_update_type == item.__class__:
                    self.mirror_update_list.remove(item)
    
#不能通过Miorrs找到MirrorAgent，只能通过MirrorAgent找到Mirror.
#MirrorAgent是一个单例模式，只有一个实例，所有已经添加的Mirror都可以通过MirrorAgent找到。
#MirrorAgent的作用是管理所有的Mirror，包括添加、删除、查找、订阅、取消订阅等操作。
        


class MirrorsAgent():
    def __init__(self,*mirrors,local_host_mode:bool=False):
        self.mirror_list:list[Mirror] = []
        for mirror in mirrors:
            self.mirror_list.append(mirror)
        self.local_host_mode = local_host_mode
        self.mqttClient = MQTTClient(self.local_host_mode)
        self.mqttClient.add_message_handle_callback(self.message_handle)
    
    def connect(self):
        # 如果mqttClient已经连接，直接返回True，否则连接 ps: mqtt为单例模式，有可能其他的组件已经连接了，所以这里需要判断一下，如果已经连接了，就不需要再次连接了，直接返回True，否则连接
        if self.mqttClient.is_connected():
            return True
        else:
            self.mqttClient.connect()
    

    def message_handle(self, client, userdata, msg):
        topic = msg.topic
        message = msg.payload.decode()
        messageDict = json.loads(message)
        for mirror in self.mirror_list:
            for mirror_update in mirror.mirror_update_list:
                if mirror_update.topic == topic:
                    mirror_update.update_with_notify(messageDict)
            for topic_handle in mirror.scheduleUpdate.ms_topic_handle_list:
                if topic_handle["topic"] == topic:
                    topic_handle["handle"](messageDict)



    def find_mirror_by_id(self,id:str) -> Mirror:
        # 查找指定id的mirror,如果没有找到，返回None
        # :param id: 要查找的mirror的id
        # :return: 找到的mirror,如果没有找到，返回None  :
        for mirror in self.mirror_list:
            if mirror.id == id:
                return mirror
        return None
    
    #添加mirrors，在使用mirror之前，需要先添加mirror，否则mirror的mirror_update的update函数不会被调用
    def add_mirrors(self,*mirrors):
        subscribe_topics_list = []
        # 订阅所有mirror里的mirror_update的topics和ScheduleUpdate的topics       
        for item in mirrors:
            self.mirror_list.append(item)
            for mirror_update in item.mirror_update_list:
                subscribe_topics_list.append(mirror_update.topic)
            #订阅ScheduleUpdate的topics
            for topic_handle in item.scheduleUpdate.ms_topic_handle_list:
                subscribe_topics_list.append(topic_handle["topic"])
            #调用所有mirror的mirror_is_added_callback函数
            item.mirror_is_added_callback(self.mqttClient.publish_message)
        #订阅所有mirror里的mirror_update的topics          
        self.mqttClient.subscribe_topics(subscribe_topics_list)


    
    #删除mirrors,在不用mirror的时候，需要删除mirror，否则mirror的mirror_update的update函数会一直被调用
    def remove_mirrors(self,*mirrors):
        subscribe_topics_list = []
        # 取消订阅所有mirror里的mirror_update的topics和ScheduleUpdate的topics
        for item in mirrors:
            self.mirror_list.remove(item)
            for mirror_update in item.mirror_update_list:
                subscribe_topics_list.append(mirror_update.topic)
            for topic_handle in item.scheduleUpdate.ms_topic_handle_list:
                subscribe_topics_list.append(topic_handle["topic"])      
        self.mqttClient.unsubscribe_topics(subscribe_topics_list)

    def add_notify_func_to_mirror_update(self,mirror_id,mirror_update_type,func):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror_update = mirror.find_mirror_update_by_type(mirror_update_type)
        mirror_update.add_notify(func)

    def remove_notify_func_from_mirror_update(self,mirror_id,mirror_update_type,func):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror_update = mirror.find_mirror_update_by_type(mirror_update_type)
        mirror_update.remove_notify(func)

    def add_notify_func_to_schedule_update(self,mirror_id,content,func):
        '''
        content: "runingTask","missionsList","scheduleState"中的一个
        func: 要添加的notify函数
        '''
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.add_notify(content,func)

    def remove_notify_func_from_schedule_update(self,mirror_id,content,func):
        '''
        content: "runingTask","missionsList","scheduleState"中的一个
        func: 要删除的notify函数
        '''
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.remove_notify(content,func)

    def request_scheduleUpdate_missions_list(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_requestMissionsList()

    def start_mission_schedule(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_start()

    def stop_mission_schedule(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_stop()

    def restart_mission_schedule(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_restart()

    def append_mission_to_schedule(self,mirror_id,missionDict:MissionInfo):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_appendMission(missionDict)

    def add_mission_to_schedule(self,mirror_id,missionDict:MissionInfo,index:int):    
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_addMission(missionDict,index)

    def delete_mission_from_schedule(self,mirror_id,index:int):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_deleteMission(index)
    
    def moveup_mission_in_schedule(self,mirror_id,index:int):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_moveupMission(index)

    def movedown_mission_in_schedule(self,mirror_id,index:int):
        mirror = self.find_mirror_by_id(mirror_id)
        if  mirror is None:
            raise Exception("mirror not found")
        mirror.scheduleUpdate.ms_movedownMission(index)
    
    def get_missons_list_from_schedule(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found")
        return mirror.scheduleUpdate.missionsList

    def get_runing_task_from_schedule(self,mirror_id):
        mirror = self.find_mirror_by_id(mirror_id)
        if mirror is None:
            raise Exception("mirror not found") 
        return mirror.scheduleUpdate.runingTask






        
  
